package org.redhat.auth.security.data.impl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.redhat.auth.security.data.DataModel;
import org.redhat.auth.security.user.UserDetailInfo;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;

public class DataModelImpl implements DataModel {

	/* JDBC URL */
	private String url;

	/* JDBC username */
	private String username;

	/* JDBC password */
	private String password;

	/* JDBC driver */
	private String driver;

	/* JDBC 获取用户信息的SQL语句 */
	private String userSql;

	/* JDBC 获取用户角色信息的SQL语句 */
	private String userRolesSql;

	/* JDBC 获取资源信息的SQL语句 */
	private String resourceSql;

	/* JDBC 测试用户名 */
	private String testUsername;

	/* JDBC 测试密码 */
	private String testPassword;

	private boolean allowTestUser;

	/** arrow data source **/
	private DataSource dataSource;

	private Map<String, Collection<ConfigAttribute>> getResourceByDataSource()
			throws Exception {
		if (dataSource == null) {
			return null;
		}
		return getResourceByConnection(dataSource.getConnection());
	}

	private Map<String, Collection<ConfigAttribute>> getResourceByDriver()
			throws Exception {
		Class.forName(driver);
		return getResourceByConnection(DriverManager.getConnection(url,
				username, password));
	}

	private Map<String, Collection<ConfigAttribute>> getResourceByConnection(
			Connection connection) throws Exception {
		Map<String, Collection<ConfigAttribute>> resources = new HashMap<String, Collection<ConfigAttribute>>();
		Statement statment = null;
		ResultSet rs = null;
		try {
			statment = connection.createStatement();
			rs = statment.executeQuery(resourceSql);
			while (rs.next()) {
				String url = rs.getString("URL");
				String roleName = rs.getString("ROLE");
				if (!roleName.startsWith("ROLE_")) {
					roleName = "ROLE_" + roleName;
				}
				if (resources.containsKey(url)) {
					ConfigAttribute ca = new SecurityConfig(roleName);
					Collection<ConfigAttribute> collection = resources.get(url);
					collection.add(ca);
					resources.put(url, collection);
				} else {
					ConfigAttribute ca = new SecurityConfig(roleName);
					Collection<ConfigAttribute> collection = new ArrayList<ConfigAttribute>();
					collection.add(ca);
					resources.put(url, collection);
				}
			}
		} finally {
			closeResultSet(rs);
			closeStatement(statment);
			closeConnection(connection);
		}
		return resources;
	}

	/**
	 * 返回数据库配置的资源保护
	 */
	public Map<String, Collection<ConfigAttribute>> getResource() {
		try {
			if (dataSource != null) {
				return getResourceByDataSource();
			}
			return getResourceByDriver();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public UserDetailInfo getUserByName(String username) {
		/* 在配置启用测试用户时,允许在开发中，持有一个测试用户，此用户不受权限限制 */
		if (isAllowTestUser()) {
			if (username.equals(testUsername)) {
				Collection<GrantedAuthority> atts = new ArrayList<GrantedAuthority>();
				UserDetailInfo user = new UserDetailInfo(testUsername,
						testPassword, true, true, true, true, atts);
				user.setTestUser(true);
				return user;
			}
		}
		return getUserInfoByName(username);
	}

	/**
	 * 获取用户信息，以JDBC方式
	 */
	private UserDetailInfo getUserInfoByName(String name) {
		try {
			if (dataSource != null) {
				return getUserInfoByNameByDataSource(name);
			}
			return getUserInfoByNameByDriver(name);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private UserDetailInfo getUserInfoByNameByDataSource(String name)
			throws Exception {
		if (dataSource == null) {
			return null;
		}
		return getUserInfoByNameByConnection(name, dataSource.getConnection());
	}

	private UserDetailInfo getUserInfoByNameByDriver(String name)
			throws Exception {
		Class.forName(driver);
		return getUserInfoByNameByConnection(name, DriverManager.getConnection(
				url, username, password));
	}

	private UserDetailInfo getUserInfoByNameByConnection(String name,
			Connection connection) throws Exception {
		UserDetailInfo user = null;
		PreparedStatement statment = null;
		ResultSet rs = null;
		try {
			statment = connection.prepareStatement(userSql);
			statment.setString(1, name);
			rs = statment.executeQuery();
			while (rs.next()) {
				Collection<GrantedAuthority> atts = getRoles(name);
				//附加属性更新
				user = new UserDetailInfo(rs.getString("USERNAME"), rs
						.getString("PASSWORD"), true, true, true, true, atts);
				user.setDescription(rs.getString("DESCRIPTION"));
				user.setEmail(rs.getString("EMAIL"));
				user.setPasswordLastUpdateDate(rs
						.getDate("PASSWORD_LAST_UPDATE_DATE"));
				user.setRealName(rs.getString("REAL_NAME"));
				user.setRegistryDate(rs.getDate("REGISTRY_DATE"));
			}
		} finally {
			closeResultSet(rs);
			closeStatement(statment);
			closeConnection(connection);
		}
		return user;
	}

	/**
	 * 获取一个用户的所有角色
	 * 
	 * @param username
	 * @return
	 */
	private Collection<GrantedAuthority> getRoles(String name) {
		Collection<GrantedAuthority> atts = new ArrayList<GrantedAuthority>();
		Connection con = null;
		PreparedStatement statment = null;
		ResultSet rs = null;
		try {
			Class.forName(driver);
			con = DriverManager.getConnection(url, username, password);
			statment = con.prepareStatement(userRolesSql);
			statment.setString(1, name);
			rs = statment.executeQuery();
			while (rs.next()) {
				String roleName = rs.getString("ROLE_NAME");
				if (!roleName.startsWith("ROLE_")) {
					roleName = "ROLE_" + roleName;
				}
				GrantedAuthority auth1 = new GrantedAuthorityImpl(roleName);
				atts.add(auth1);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (rs != null)
					rs.close();
				if (statment != null)
					statment.close();
				if (con != null)
					con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return atts;
	}

	private static void closeConnection(Connection conn) {
		try {
			conn.close();
		} catch (Exception e) {
		}
	}

	private static void closeResultSet(ResultSet resultSet) {
		try {
			resultSet.close();
		} catch (Exception e) {
		}
	}

	private static void closeStatement(Statement stmt) {
		try {
			stmt.close();
		} catch (Exception e) {
		}
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUserSql() {
		return userSql;
	}

	public void setUserSql(String userSql) {
		this.userSql = userSql;
	}

	public String getUserRolesSql() {
		return userRolesSql;
	}

	public void setUserRolesSql(String userRolesSql) {
		this.userRolesSql = userRolesSql;
	}

	public String getTestUsername() {
		return testUsername;
	}

	public void setTestUsername(String testUsername) {
		this.testUsername = testUsername;
	}

	public String getTestPassword() {
		return testPassword;
	}

	public void setTestPassword(String testPassword) {
		this.testPassword = testPassword;
	}

	public String getResourceSql() {
		return resourceSql;
	}

	public void setResourceSql(String resourceSql) {
		this.resourceSql = resourceSql;
	}

	public boolean isAllowTestUser() {
		return allowTestUser;
	}

	public void setAllowTestUser(boolean allowTestUser) {
		this.allowTestUser = allowTestUser;
	}

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public boolean validatePassword(String username, String password) {
		return false;
	}

}
